﻿// Autore: Alessio Parma

using System;
using System.Data.Objects;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using RecipesManager.Data.Implementation;

namespace RecipesManager.Data
{
    public static class Manager
    {
        const int DbVersion = 2;

        static readonly ICategories CachedCategories;
        static readonly IEvents CachedEvents;
        static readonly IGuests CachedGuests;
        static readonly IIngredients CachedIngredients;
        static readonly IMeasures CachedMeasures;
        static readonly IRecipes CachedRecipes;
        static readonly IShops CachedShops;
        static Context _ctx;

        static readonly Func<ObjectSet<ShoppingList>> ShoppingListSet = () => _ctx.ShoppingLists;
        internal static readonly Func<ObjectSet<Category>> CategorySet = () => _ctx.Categories;
        internal static readonly Func<ObjectSet<Ingredient>> IngredientSet = () => _ctx.Ingredients;
        internal static readonly Func<ObjectSet<Event>> EventSet = () => _ctx.Events;
        internal static readonly Func<ObjectSet<Guest>> GuestSet = () => _ctx.Guests;
        internal static readonly Func<ObjectSet<Measure>> MeasureSet = () => _ctx.Measures;
        internal static readonly Func<ObjectSet<Recipe>> RecipeSet = () => _ctx.Recipes;
        internal static readonly Func<ObjectSet<Shop>> ShopSet = () => _ctx.Shops;
        internal static readonly Func<ObjectSet<Step>> StepSet = () => _ctx.Steps;
        internal static readonly Func<ObjectSet<Trial>> TrialSet = () => _ctx.Trials;

        static Manager()
        {
            CachedCategories = new CategoryObjects();
            CachedEvents = new EventObjects();
            CachedGuests = new GuestObjects();
            CachedIngredients = new IngredientObjects();
            CachedMeasures = new MeasureObjects();
            CachedRecipes = new RecipeObjects();
            CachedShops = new ShopObjects();

            _ctx = new Context();
        }

        public static bool DatabaseIsReady
        {
            get
            {
                try
                {
                    // ReSharper disable UnusedVariable
                    var sl = _ctx.ShoppingLists.First();
                    var v = sl.DbVersion;
                    if (v == 1) UpgradeDbFromV1ToV2();
                    // ReSharper restore UnusedVariable
                    return true;
                }
                catch (Exception)
                {
                    return false;
                }
            }
        }

        public static ICategories Categories
        {
            get { return CachedCategories; }
        }

        public static IEvents Events
        {
            get { return CachedEvents; }
        }

        public static IGuests Guests
        {
            get { return CachedGuests; }
        }

        public static IIngredients Ingredients
        {
            get { return CachedIngredients; }
        }

        public static IMeasures Measures
        {
            get { return CachedMeasures; }
        }

        public static IRecipes Recipes
        {
            get { return CachedRecipes; }
        }

        public static IShoppingList ShoppingList
        {
            get { return ShoppingListSet().First(); }
        }

        public static IShops Shops
        {
            get { return CachedShops; }
        }

        public static void ClearCache()
        {
            _ctx = null;
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            _ctx = new Context();
        }

        public static void ClearDatabase()
        {
            _ctx.DeleteDatabase();
            ClearCache();
            _ctx.CreateDatabase();
            _ctx.ShoppingLists.AddObject(Implementation.ShoppingList.New(DbVersion));
            _ctx.SaveChanges();
            _ctx.ExecuteStoreCommand("ALTER TABLE Categories ADD CONSTRAINT UN_Categories UNIQUE (Name)");
            _ctx.ExecuteStoreCommand("ALTER TABLE Guests ADD CONSTRAINT UN_Guests UNIQUE (Name)");
            _ctx.ExecuteStoreCommand("ALTER TABLE Ingredients ADD CONSTRAINT UN_Ingredients UNIQUE (Name)");
            _ctx.ExecuteStoreCommand("ALTER TABLE Measures ADD CONSTRAINT UN_Measures UNIQUE (Name)");
            _ctx.ExecuteStoreCommand("ALTER TABLE Recipes ADD CONSTRAINT UN_Recipes UNIQUE (Name)");
            _ctx.ExecuteStoreCommand("ALTER TABLE Shops ADD CONSTRAINT UN_Shops UNIQUE (Name)");
            _ctx.ExecuteStoreCommand("CREATE INDEX IX_Event_Name ON Events(Name)");
        }

        public static void FillDatabase()
        {
            ClearDatabase();
            Loader.LoadDatabase();
        }

        internal static void SaveChanges()
        {
            _ctx.SaveChanges();
        }

        static void UpgradeDbFromV1ToV2()
        {
            // Correzione delle date per i tre eventi di default
            _ctx.Events.First(e => e.Id == 1).Date = new DateTime(2013, 03, 31); // Pasqua 2013
            _ctx.Events.First(e => e.Id == 2).Date = new DateTime(2013, 12, 25); // Pranzo di Natale 2013
            _ctx.Events.First(e => e.Id == 3).Date = new DateTime(2013, 12, 31); // Ultimo dell'anno 2013
            // Incremento di versione e salvataggio dei cambiamenti
            _ctx.ShoppingLists.First().DbVersion = 2;
            _ctx.SaveChanges();
        }
    }

    static class ExtensionMethods
    {
        static readonly TextInfo ItalianTextInfo = CultureInfo.GetCultureInfo("it-IT").TextInfo;

        [Pure]
        public static string ToKey(this string s)
        {
            return ItalianTextInfo.ToTitleCase(s.ToLower());
        }
    }
}